package com.zf.common.web.trace_log;

import com.zf.common.web.config.BasicConstants;
import org.slf4j.MDC;
import org.springframework.core.Ordered;
import org.springframework.stereotype.Component;
import org.springframework.util.StringUtils;

import javax.servlet.*;
import javax.servlet.http.HttpServletRequest;
import java.io.IOException;
import java.util.UUID;

/**
 * 将请求的全局唯一的追踪id 加入日志的上下文中(MDC)，配合日志配置 %X{KEY} ，可以在同一个线程内的所有日志输出上都携带同一个追踪id，方便追踪调用的整个链路
 * 【 MDC和线程相关，内部使用ThreadLocal实现，加入MDC中的内容，在线程的整个生命周期内都能在当前线程内的访问到，除非手动移除】
 */
// 无需 @WebFilter 注解，只需要将LogTraceIdFilter交给spring容器管理即可；
// 如果使用 @WebFilter，则需要使用 @ServletComponentScan("com.zf.common.log.trace_log") 将Filter加入spring容器
// @WebFilter(filterName = "logTraceIdFilter", urlPatterns = {"/**"})
@Component
public class LogTraceIdFilter implements Filter, Ordered {

    @Override
    public void init(FilterConfig filterConfig) throws ServletException {
    }

    @Override
    public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
        HttpServletRequest request = (HttpServletRequest) servletRequest;
        // 从请求头中获取 TRACE_ID, 如果没有获取到，则生成一个（如果是gateway作为统一入口，一般会在gateway中实现一个GlobalFilter在请求头中写入 TRACE_ID）
        String traceId = request.getHeader(BasicConstants.TRACE_ID);
        if (!StringUtils.hasText(traceId)) {
            // 如果没有获取到则生成一个
            traceId = UUID.randomUUID().toString();
            // 将获取的请求头写入 request的Attribute 中，因为 HttpServletRequest 对象不能修改请求头；
            // 放入 request的Attribute 中 可以在其他地方调用时，防止因为请求头中不存在就无法获取 TRACE_ID
            request.setAttribute(BasicConstants.TRACE_ID, traceId);
        }
        // 将  TRACE_ID 写入日志上下文中， 也可以放入其他的内容，在日志文件中通过 %X{TRACE_ID} 获取值，这样就可以在同一个线程内的所有日志输出上都携带同一个追踪id，方便追踪调用的整个链路
        MDC.put(BasicConstants.TRACE_ID, traceId);
        // MDC.put("IP", request.getRemoteAddr());
        try {
            // 继续调用其他filter
            filterChain.doFilter(servletRequest, servletResponse);
        } finally {
            // 请求结束后，移除 TRACE_ID
            MDC.remove(BasicConstants.TRACE_ID);
            request.removeAttribute(BasicConstants.TRACE_ID);
        }
    }

    @Override
    public void destroy() {
    }

    @Override
    public int getOrder() {
        return 0;
    }
}
